
use std::{env::set_var, fs::remove_file, path::{PathBuf, absolute}, sync::mpsc::channel};

use clap::Args;
use serde::{Deserialize, Serialize};
use wtask_base::{cmd::file_to_x_power, error::{WError, WResult}};

use crate::{
    basic::Empty,
    task::utils::{
        func::task_local_simple_check,
        meta::{CmdMeta, TASK_RUN_LOG_END, TASK_RUN_LOG_PREFIX, TASK_RUN_META_FILE, TaskStatus, WTASK_INSTALL_FILES}
    },
    utils::{AppTrait}
};







#[derive(Debug, Clone, Serialize, Deserialize, Args)]
pub struct AppTaskCheck {
    /// 任务的文件夹路径
    /// 检查任务语法后通过podman进行安装测试
    pub path: PathBuf,

    /// 不进行安装测试
    #[arg(long)]
    pub no_install: bool,

    /// 安装测试成功后保留测试的日志
    #[arg(long)]
    pub keep_log: bool,
}

impl AppTaskCheck {
    pub fn run_check<M: FnMut(bool, String) + Send + Sync>(self, mut msg_func: M) -> WResult<()> {
        let dir = absolute(self.path)?;
        let task_id = dir.file_name().unwrap().to_str().unwrap();
        if !dir.is_dir() {
            return Err(WError::Path);
        }
        match task_local_simple_check(&dir, &mut msg_func) {
            Ok(false) => {},
            Ok(true) => {
                return Err(WError::Stop);
            },
            Err(e) => {
                return Err(WError::DataError(format!("{task_id} task syntax check error: {e}")));
            }
        }
        if self.no_install {
            return Ok(());
        }
        let file_install = dir.join(WTASK_INSTALL_FILES[3]);
        file_to_x_power(&file_install)?;
        unsafe { set_var("WTASK_FORCE_PODMAN", "1") };
        msg_func(false, "task test install ...".to_owned());
        let (rx_log, tx_log) = channel::<(bool, String)>();
        let msg_func_cmd = Box::new(move | b, v | { let _ = rx_log.send((b, v)); });
        let cmd_ret = CmdMeta::new(
            format!("{}.test_install", task_id),
            "任务检查".to_owned(),
            None,
            None,
            format!("task {} install test", task_id),
            format!("{}@Test", task_id),
            vec![file_install.to_string_lossy().to_string()],
            dir.clone(),
            None
        ).run_cmd_task(msg_func_cmd)?;
        while let Ok((b, v)) = tx_log.recv() {
            msg_func(b, v);
        }
        let meta = cmd_ret.handle.join().map_err(|_| WError::Join)?;
        match meta {
            Ok(v) if v.status.eq(&TaskStatus::Success) => {
                if !self.keep_log {
                    let file_log = dir.join(format!("{}{:?}{}", TASK_RUN_LOG_PREFIX, v.status, TASK_RUN_LOG_END));
                    let file_meta = dir.join(TASK_RUN_META_FILE);
                    let _ = remove_file(&file_log);
                    let _ = remove_file(&file_meta);
                }
                msg_func(false, format!("{task_id} task test install complete"));
            },
            Ok(_) => {
                return Err(WError::DataError(format!("{task_id} task test install error, please check install log")));
            },
            Err(e) => {
                return Err(WError::DataError(format!("{task_id} task test install error: {e}")));
            }
        }
        Ok(())
    }
}


impl AppTrait for AppTaskCheck {
    type Status = ();
    type Res = Empty;

    fn client_handle_before<M: FnMut(bool, String) + Send + Sync>(self, mut msg_func: M, _status: Option<Self::Status>) -> WResult<(Option<Self>, Option<Self::Status>)> {
        if let Err(e) = self.run_check(&mut msg_func) {
            msg_func(true, e.to_string());
        }
        Ok((None, None))
    }
}
